// barcode.js
// 在 UniApp 项目中使用的条码渲染器（兼容 H5 + 小程序）
// 使用 createCanvasContext + canvasToTempFilePath

import barcodes from './barcodes/index.js';

let barcode = {};

(function () {
	barcode = function (canvasSelector, options, onCanvasSize, onResult) {
		let newOptions = { ...options };

		// 修正 margin
		fixMargin(newOptions);

		// 验证输入
		if (!newOptions.text || newOptions.text === '') {
			console.warn('条码内容为空');
			onResult?.(null);
			return false;
		}

		// 获取 canvasId：支持选择器字符串或直接传入 id
		let canvasId;
		if (typeof canvasSelector === 'string') {
			// 假设传入的是 #id 或 .class，我们只取 id（UniApp 中 canvasId 是字符串 id）
			canvasId = canvasSelector.replace('#', '').replace('.', '');
		} else if (canvasSelector && canvasSelector.id) {
			canvasId = canvasSelector.id;
		} else {
			console.error('无效的 canvas 标识:', canvasSelector);
			onResult?.(null);
			return false;
		}

		// 自动处理CODE128格式的起始字符
		if (
			newOptions.format &&
			newOptions.format.toUpperCase().includes('CODE128')
		) {
			handleCode128StartChars(newOptions);
		}

		// 创建 UniApp Canvas Context
		const ctx = uni.createCanvasContext(canvasId, this);
		if (!ctx) {
			console.error('无法创建 canvas 上下文:', canvasId);
			onResult?.(null);
			return false;
		}

		// 创建编码器
		const Format = barcodes[newOptions.format.toUpperCase()];
		if (!Format) {
			console.error(`不支持的条码格式: ${newOptions.format}`);
			onResult?.(null);
			return false;
		}

		const encoder = new Format(newOptions.text, newOptions);
		const encodings = encoder.encode();

		// 计算布局（使用临时 measureText 模拟）
		const fixedEncodings = fixEncodings(encodings, newOptions, ctx);

		// 设置 canvas 实际宽高（防止模糊），确保最小尺寸
		const canvasWidth = Math.max(fixedEncodings.width, 100);
		const canvasHeight = Math.max(fixedEncodings.height, 50);

		// 回调返回 canvas 尺寸
		onCanvasSize?.({
			width: canvasWidth,
			height: canvasHeight,
		});

		// 渲染条码
		drawCanvas.render(ctx, newOptions, fixedEncodings, () => {
			// 渲染完成后导出图片
			// 添加延时确保绘制完成
			setTimeout(() => {
				try {
					uni.canvasToTempFilePath({
						canvasId,
						x: 0,
						y: 0,
						width: canvasWidth,
						height: canvasHeight,
						destWidth: canvasWidth,
						destHeight: canvasHeight,
						success: res => {
							onResult?.(res.tempFilePath);
						},
						fail: err => {
							console.error('canvasToTempFilePath 失败:', err);

							// 容错处理：即使导出失败也返回一个空的结果而不是报错
							onResult?.(null);
						},
					});
				} catch (e) {
					console.error('导出图片时发生错误:', e);

					// 容错处理：捕获异常并返回空结果
					onResult?.(null);
				}
			}, 150); // 延时确保绘制完成
		});

		return true;
	};

	// 处理CODE128格式的起始字符
	function handleCode128StartChars(options) {
		const format = options.format.toUpperCase();
		const text = options.text;

		// 如果已经有起始字符，则不需要处理
		if (text.charCodeAt(0) >= 200 && text.charCodeAt(0) <= 202) {
			return;
		}

		// 根据格式自动添加起始字符
		switch (format) {
			case 'CODE128A':
				options.text = String.fromCharCode(200) + text;
				break;
			case 'CODE128B':
				options.text = String.fromCharCode(201) + text;
				break;
			case 'CODE128C':
				// CODE128C需要偶数位数字
				if (/^\d+$/.test(text) && text.length % 2 === 0) {
					options.text = String.fromCharCode(202) + text;
				} else {
					// 不满足条件时回退到CODE128B
					options.format = 'CODE128B';
					options.text = String.fromCharCode(201) + text;
				}
				break;
			case 'CODE128':
			case 'CODE128AUTO':
				// 对于自动模式，默认使用CODE128B
				options.format = 'CODE128B';
				options.text = String.fromCharCode(201) + text;
				break;
		}
	}

	// 修正 margin
	function fixMargin(options) {
		const margin = options.margin !== null ? options.margin : 10;
		options.marginTop =
			options.marginTop !== null ? options.marginTop : margin;
		options.marginBottom =
			options.marginBottom !== null ? options.marginBottom : margin;
		options.marginLeft =
			options.marginLeft !== null ? options.marginLeft : margin;
		options.marginRight =
			options.marginRight !== null ? options.marginRight : margin;
	}

	// 计算编码布局（含文本宽度测量）
	function fixEncodings(encoding, options, ctx) {
		const encodingArr = Array.isArray(encoding) ? [...encoding] : [encoding];
		let totalWidth = (options.marginLeft || 0) + (options.marginRight || 0);
		let maxHeight = 0;

		// 在 UniApp 中模拟 measureText
		const fontSize = options.fontSize || 16;
		try {
			ctx.setFontSize(fontSize);
		} catch (e) {
			console.warn('设置字体大小失败，使用默认值');
		}

		const resultEncodings = encodingArr.map(enc => {
			const text = enc.text || '';
			let textWidth = 0;
			if (options.displayValue && text) {
				// UniApp measureText 兼容处理
				try {
					if (ctx.measureText) {
						const res = ctx.measureText(text);
						textWidth = res.width || 0;
					} else {
						// 简易估算（中文字符按 fontSize 宽度，英文按 0.6 倍）
						textWidth = [...text].reduce((w, c) => {
							return (
								w + (/[\u4e00-\u9fa5]/.test(c) ? fontSize : fontSize * 0.6)
							);
						}, 0);
					}
				} catch (e) {
					console.warn('测量文本宽度失败，使用默认值');
					textWidth = text.length * fontSize * 0.6;
				}
			}

			const barcodeWidth = (enc.data?.length || 0) * (options.width || 2);
			const maxWidth = Math.max(textWidth, barcodeWidth, 50); // 最小宽度50

			let barcodePadding = 0;
			if (options.displayValue && barcodeWidth < textWidth) {
				switch (options.textAlign) {
					case 'left':
						barcodePadding = 0;
						break;
					case 'right':
						barcodePadding = textWidth - barcodeWidth;
						break;
					default:
						// 改进计算方式，确保居中时两边的间距相等
						barcodePadding = (textWidth - barcodeWidth) / 2;
						break;
				}
			}

			const height =
				((enc.options?.height ?? options.height) || 100) +
				(options.marginTop || 0) +
				(options.marginBottom || 0) +
				(options.displayValue && text
					? (options.fontSize || 16) + (options.textMargin || 2)
					: 0);

			maxHeight = Math.max(maxHeight, height, 30); // 最小高度30

			return {
				...enc,
				width: Math.ceil(maxWidth),
				height,
				barcodePadding,
			};
		});

		const totalEncodingsWidth = resultEncodings.reduce(
			(sum, enc) => sum + (enc.width || 0),
			0
		);

		// 确保最小尺寸
		const minWidth = Math.max(totalWidth + totalEncodingsWidth, 100);
		const minHeight = Math.max(maxHeight, 50);

		return {
			encodings: resultEncodings,
			width: minWidth,
			height: minHeight,
		};
	}

	// 渲染逻辑（适配 UniApp ctx）
	const drawCanvas = {
		render(ctx, options, encoding, done) {
			try {
				ctx.save();
				this.prepare(ctx, options, encoding);

				let currentX = 0;
				encoding.encodings.forEach(enc => {
					this.barcode(ctx, options, enc, currentX);
					if (options.displayValue) {
						this.text(ctx, options, enc, currentX);
					}
					currentX += enc.width || 0;
				});

				ctx.restore();
				ctx.draw(false, done); // 执行绘制，完成后调用 done
			} catch (e) {
				console.error('绘制条码时出错:', e);
				// 即使出错也要调用done以避免阻塞
				if (done) done();
			}
		},

		prepare(ctx, options, encoding) {
			try {
				// 背景
				if (options.background) {
					ctx.setFillStyle(options.background);
					ctx.fillRect(
						0,
						0,
						Math.max(encoding.width, 100),
						Math.max(encoding.height, 50)
					);
				}
				ctx.translate(options.marginLeft || 0, 0);
			} catch (e) {
				console.warn('准备绘制环境时出错:', e);
			}
		},

		barcode(ctx, options, encoding, offsetX) {
			try {
				const binary = encoding.data || '';
				const yFrom =
					options.textPosition === 'top'
						? (options.marginTop || 0) +
						  (options.fontSize || 16) +
						  (options.textMargin || 2)
						: options.marginTop || 0;

				const height = (encoding.options?.height ?? options.height) || 100;
				ctx.setFillStyle(options.lineColor || '#000000');

				for (let b = 0; b < binary.length; b++) {
					const x =
						(offsetX || 0) +
						b * (options.width || 2) +
						(encoding.barcodePadding || 0);
					const bit = binary[b];
					if (bit === '1') {
						ctx.fillRect(x, yFrom, options.width || 2, height);
					} else if (bit !== '0') {
						const subHeight = height * parseFloat(bit || 0);
						ctx.fillRect(
							x,
							yFrom + (height - subHeight),
							options.width || 2,
							subHeight
						);
					}
				}
			} catch (e) {
				console.warn('绘制条码时出错:', e);
			}
		},

		text(ctx, options, encoding, offsetX) {
			try {
				const text = encoding.text;
				if (!text) return;

				const fontSize = (encoding.options?.fontSize ?? options.fontSize) || 16;
				const textAlign =
					(encoding.options?.textAlign ?? options.textAlign) || 'center';

				ctx.setFontSize(fontSize);
				ctx.setFillStyle(options.fontColor || '#000000');

				let x, y;
				if (options.textPosition === 'top') {
					y = (options.marginTop || 0) + fontSize;
				} else {
					y =
						(options.height || 100) +
						(options.textMargin || 2) +
						(options.marginTop || 0) +
						fontSize;
				}

				switch (textAlign) {
					case 'left':
						x = (offsetX || 0) + (encoding.barcodePadding || 0);
						ctx.setTextAlign('left');
						break;
					case 'right':
						x = (offsetX || 0) + (encoding.width || 0) - 1;
						ctx.setTextAlign('right');
						break;
					default:
						x = (offsetX || 0) + (encoding.width || 0) / 2;
						ctx.setTextAlign('center');
						break;
				}

				ctx.fillText(text, x, y);
			} catch (e) {
				console.warn('绘制文本时出错:', e);
			}
		},
	};
})();

export default barcode;
